
from rest_framework.viewsets import ViewSet
from rest_framework.permissions import IsAuthenticated
from course.models import Course,CourseExpire
from rest_framework.response import Response
from rest_framework import status
from django_redis import get_redis_connection

import logging
log = logging.getLogger("django")

class CartAPIView(ViewSet):
    """
        购物车视图类
        购物车不再从mysql中拿数据，所以不必用5个类
            只需要用基础视图类 APIView，但是如果这1个类下接收多个不同get请求，会写很多个
            需要用viewsets ,这样可以接受多个请求，不同请求方式对应不同方法
    """

    permission_classes = [IsAuthenticated,]     #限制访问的时候必须是已经登录的用户
    def add(self,request):
        """
            添加商品到购物车的接口，add可以自定义其他的
            接收post请求
        """
        #1，接收客户端的参数[用户id，课程id，勾选状态，有效期选项]
        course_id = request.data.get("course_id")
        user_id = request.user.id   #request.user用户模型拿id直接 request.user.id获取
        # user_id = 1 #由于测试暂时给1
        #设置勾选默认值，默认勾选了，后期用户自己勾在改
        selected = True
        # 有效期 默认为0
        expire = 0

        #2，校验参数(防止恶意数据)
        try:
            course = Course.objects.get(is_show=True,is_deleted=False,id=course_id)
        except Course.DoesNotExist: #上面get拿不到数据就报错
            #课程不存在,返回错误信息
            return  Response({"message":"参数有误课程不存在"},status=status.HTTP_400_BAD_REQUEST)

        #3，获取redis链接对象
        redis_conn = get_redis_connection("cart")

        #4，保存数据到redis,用事物存储
        try:
            pipe = redis_conn.pipeline()
            pipe.multi()    #开启事物
            #存hash数据
            pipe.hset("cart_%s"%(user_id),course_id,expire)
            #存集合数据 由于键唯一，所以不用cart_xx用selected_xx,凡是打钩的都存这里
            pipe.sadd("selected_%s"%(user_id),course_id)
            pipe.execute()

            # 查询购物车商品总数
            course_len = redis_conn.hlen("cart_%s" % (user_id))

        except: #如果出错记录日志，抛出的是 RedisError ，会记录异常日志utils/execptions.py的异常日志中去
            log.error("购物车数据存储错误")
            return Response({"message": "购物车添加商品失败"}, status=status.HTTP_507_INSUFFICIENT_STORAGE)

        #5，返回结果【返回当前购物车商品的总数】

        return Response({"message":"购物车商品添加成功","cart_length":course_len})


    def list(self,request):
        """购物车商品的列表"""
        # user_id = request.user.id
        user_id = 1 #测试期间给1
        #从redis中读ß取数据
        redis_conn = get_redis_connection("cart")
        #拿到所有hash和集合的数据，取出来都是bytes类型
        cart_bytes_dict = redis_conn.hgetall("cart_%s" %(user_id))
        selected_bytes_dict = redis_conn.smembers("selected_%s" %(user_id))

        #循环从mysql根据课程id提取对应商品信息【商品id，封面图片，商品标题】
        data = []
        from luffyapi.settings import constants
        for course_id_bytes,expire_id_bytes in cart_bytes_dict.items():
            course_id = int(course_id_bytes.decode())
            expire_id = int(expire_id_bytes.decode())
            try:
                course = Course.objects.get(is_show=True,is_deleted=False,pk=course_id)
            except Course.DoesNotExist:
                continue


            data.append({
                #当商品id在勾选set去重的集合中，表名是勾选的
                "selected" :True if course_id_bytes in selected_bytes_dict else False,
                #从数据库的queryset对象取course_img 通过.url取到相对路径和域名拼接
                "course_img" : constants.SERVER_IMAGE_DOMAIN + course.course_img.url,
                "name":course.name,
                "id":course_id,
                "expire_id":expire_id,
                "expire_list":course.expire_list,
                "price":course.real_price(expire_id),
            })
        return Response(data)

    def change_selected(self,request):
        """前端购物车支付前，支付哪些课程前面的勾选状态切换的接口"""
        user_id = request.user.id
        selected = request.data.get("selected")
        course_id = request.data.get("course_id")
        try:
            Course.objects.filter(is_show=True,is_deleted=False,id=course_id)
        except Course.DoesNotExist:
            return Response({"message":"参数有误，商品不存在"},status=status.HTTP_400_BAD_REQUEST)

        redis_conn = get_redis_connection("cart")
        # 判断selected是给集合 True添加，还是False删除
        if selected:
            redis_conn.sadd("selected_%s"%(user_id),course_id)
            return Response({"message": "勾选"})
        else:
            redis_conn.srem("selected_%s"%(user_id),course_id)
            return Response({"message": "取消勾选"})


    def change_expire(self, request):
        """切换购物车商品的有效期选项"""
        user_id = request.user.id
        expire_id = request.data.get("expire_id")
        course_id = request.data.get("course_id")
        try:
            # 判断课程是否存在
            course = Course.objects.get(is_show=True, is_deleted=False, id=course_id)
            # 判断课程的有效期选项是0还是其他的数值，如果是其他数值，还要判断是否存在于有效期选项表中
            if expire_id > 0:
                epxire_item = CourseExpire.objects.filter(is_show=True, is_deleted=False, id=expire_id)
                #如果查询结果不存在 返回False
                if not epxire_item:
                    raise Course.DoesNotExist()
        except Course.DoesNotExist:
            return Response({"message": "参数有误！当前商品课程不存在或者不能存在的有效期！"}, status=status.HTTP_400_BAD_REQUEST)

        #直接更新redis
        redis_conn = get_redis_connection("cart")
        redis_conn.hset("cart_%s" % user_id, course_id, expire_id)

        real_price = course.real_price(expire_id)
        return Response({"message": "切换课程有效期成功！","real_price":real_price})


    def del_cart(self,request):
        """从购物车中删除商品信息"""
        user_id = request.user.id
        course_id = request.query_params.get("course_id")   #query_params是delete请求获取请求体
        try:
            Course.objects.get(is_show=True, is_deleted=False, id=course_id)
        except Course.DoesNotExist:
            return Response({"message":"参数有误！当前商品课程不存在！"}, status=status.HTTP_400_BAD_REQUEST)

        redis_conn = get_redis_connection("cart")

        #通过redis事物执行多条redis语句
        pipe = redis_conn.pipeline()
        #开启管道
        pipe.multi()
        #吧命令放到管道中
        #删除hash数据(商品以及有效期数据)还需要删除集合数据(商品勾选状态数据)  hdel和srem方法
        pipe.hdel('cart_%s' % user_id, course_id)
        pipe.srem("selected_%s" % user_id, course_id)
        #管道中执行所有redis语句
        pipe.execute()

        return Response({"message":"删除商品成功！"})



    def get_selected_course(self,request):
        """获取购物车中勾选的商品列表"""
        #获取用户id
        user_id = request.user.id
        #获取redis信息
        redis_conn = get_redis_connection("cart")

        # 获取勾选商品
        data = []   #商品列表
        total_price = 0 #勾选商品总价格
        #获取购物车中所有商品数据
        cart_bytes_dict = redis_conn.hgetall("cart_%s" % (user_id))
        selected_bytes_dict = redis_conn.smembers("selected_%s" % (user_id))

        for course_id_bytes, expire_id_bytes in cart_bytes_dict.items():
            course_id = int(course_id_bytes.decode())
            expire_id = int(expire_id_bytes.decode())
            if course_id_bytes in selected_bytes_dict:
                try:
                    course = Course.objects.get(is_show=True, is_deleted=False, pk=course_id)
                except Course.DoesNotExist:
                    continue
                # 判断课程有效期，获取课程原价
                origin_price = course.price
                expire_text = "永久有效"
                try:
                    # 根据不同有效期，获取课程原价
                    if expire_id > 0:
                        # 查找到CourseExpire 表中
                        courseexpire = CourseExpire.objects.get(id=expire_id)
                        origin_price = courseexpire.price
                        expire_text = courseexpire.expire_text
                except CourseExpire.DoesNotExist:
                    pass
                # 默认真实价格是原价 如果没有参与活动，就代表是原价
                from luffyapi.settings import constants
                real_price = course.real_price(expire_id)
                data.append({
                    # 从数据库的queryset对象取course_img 通过.url取到相对路径和域名拼接
                    "course_img": constants.SERVER_IMAGE_DOMAIN + course.course_img.url,
                    "name": course.name,
                    "id": course_id,
                    "discount_name":course.discount_name,
                    "expire_text": expire_text,
                    "real_price": real_price,
                    "origin_price":"%.2f"%(origin_price),
                })
                total_price += float(real_price)
        #返回结果
        return Response({"course_list":data,"total_price":total_price})

